package cz.acies.uniql.sql.element;

import java.util.ArrayList;

import org.dom4j.Element;
import org.dom4j.dom.DOMElement;


import cz.acies.uniql.dialect.Dialect;
import cz.acies.uniql.sql.UniQLExpr;

public class UniQLJoin extends UniQLExpr
{
	private static final long serialVersionUID = -4739985719792924134L;

	/** The Constant INNER_JOIN. */
	public static final int INNER_JOIN = 0;
	
	/** The Constant LEFT_JOIN. */
	public static final int LEFT_JOIN = 1;
	
	/** The Constant RIGHT_JOIN. */
	public static final int RIGHT_JOIN = 2;
	
	/** The Constant FULL_JOIN. */
	public static final int FULL_JOIN = 3;

	/** The Constant CROSS_JOIN. */
	public static final int CROSS_JOIN = 4;
	
	/** The Constant STRAIGHT_JOIN. */
	public static final int STRAIGHT_JOIN = 5;
	
	/** The Constant NATURAL_JOIN. */
	public static final int NATURAL_JOIN = 6;
			
	private int type;
	private UniQLExpr from;
	private UniQLCriterion criterions;
	
	/**
	 * Konstruktor tridy UniQLJoin.
	 */
	public UniQLJoin() {
		super();
		this.type = 1;		
	}

	/**
	 * Konstruktor tridy UniQLJoin.
	 * 
	 * @param dialect - dialect
	 */
	public UniQLJoin(Dialect dialect) {
		super(dialect);
		this.type = 1;
	}
	
	/**
	 * Konstruktor tridy UniQLJoin.
	 * 
	 * @param type - type
	 */
	public UniQLJoin(int type) {
		super();
		this.type = type;
	}

	/**
	 * Konstruktor vytvori nonou instanci objektu UniQLJoin s vlastnostmi
	 * objektu zadaneho jako parametr konstruktoru.
	 *  
	 * @param orderBy
	 */
	public UniQLJoin(UniQLJoin expr) throws Exception {
		this();
		UniQLJoin clone = (UniQLJoin)expr.clone();
		
		setId(clone.getId());
		setName(clone.getName());
		setDialect(clone.getDialect());
		setAliasMap(clone.getAliasMap());

		addFrom(clone.getFrom());  
		setCriterions((UniQLCriterion) clone.getCriterions());
		setType(clone.getType());
	}
	
	// -----  -----------------------------------------------------------------
	
	public boolean equalsWithoutAlias(UniQLJoin o) throws Exception {
		if (!(o.getFrom() instanceof UniQLFrom)) return false;
		if (!(this.getFrom() instanceof UniQLFrom)) return false;
		
		String tblName1 = this.getFrom().getName();
		String tblName2 = o.getFrom().getName();
		if (!tblName1.equals(tblName2)) return false;
		
		if (!(o.getCriterions() instanceof UniQLCriterion)) {
			return false; 
		} else {
			if (!(this.criterions instanceof UniQLCriterion)) return false;
			else {
				//o.getCriterions().setDialect(this.getDialect());
				String xml1 = this.criterions.transToXML();
				String xml2 = o.getCriterions().transToXML();
				
				return xml1.equals(xml2);
				//this.getCriterions().setDialect(this.getDialect());
			}
		}
		//return true;
	}

	// -----  ------------------  ---------------------------------------------
	
	/* (non-Javadoc)
	 * @see cz.acies.uniql.sql.UniQLExpr#translate()
	 */
	public String translate() throws Exception {
		StringBuffer sb = new StringBuffer();
		sb.append(getTypeString()).append(" JOIN ");
		if (this.from != null) {
			sb.append(getFrom().translate()+" ON ");
			if (criterions != null) {
				criterions.setUniQLProperties(this);
				sb.append(criterions.translate());
			}
		}
		return sb.toString();
	}

	// -----  ------------------  ---------------------------------------------
	
	/**
	 * Metoda vymaze vsechny dosud pridane podmiky.
	 */
	public UniQLJoin clear() {
		criterions.clear(); criterions = null;
		return this;
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Object#clone()
	 */
	@Override
	public Object clone() throws CloneNotSupportedException {
		Object clone = super.clone();
		assert clone instanceof UniQLJoin;
		((UniQLJoin) clone).setCriterions((UniQLCriterion)criterions.clone());
		((UniQLJoin) clone).setType(type);
		return clone;
	}

	// ----- Getter & Setter --------------------------------------------------
	
	/**
	 * Prida novy objekt condition. U teto metody je mezi pridavany condition a 
	 * pripadny dalsi pridavany condition, automaticky dolnen operator AND.   
	 * 
	 * @param cf - cf
	 * 
	 * @return vraci objekt typu UniQLJoin
	 */
	public UniQLJoin addCondition(UniQLCondition cf) {
		if (this.criterions == null) this.criterions = new UniQLCriterion();
		this.criterions.setDialect(this.getDialect());
		this.criterions.addCondition(cf);
		return this;
	}
	
	/**
	 * Metodat toto�n� s metodou addCondition(UniQLCondition cf).
	 * @param cf
	 * @return
	 */
	public UniQLJoin add(UniQLCondition cf) {
		return addCondition(cf);
	}
	
	/**
	 * Prida novy objekt condition a oprerator, ktery bude ve vyslednem dotazu za
	 * pridavanou podminkou.
	 * 
	 * @param cf - cf
	 * @param operator - operator
	 * 
	 * @return vraci objekt typu UniQLJoin
	 */
	public UniQLJoin addCondition(UniQLCondition cf, String operator) {
		if (this.criterions == null) this.criterions = new UniQLCriterion();
		this.criterions.setDialect(this.getDialect());
		this.criterions.addCondition(cf, operator);
		return this;
	}
	
	/**
	 * Metoda toto�n� s metodou addCondition(UniQLCondition cf, String operator).
	 * @param cf
	 * @param operator
	 * @return
	 */
	public UniQLJoin add(UniQLCondition cf, String operator) {
		return addCondition(cf, operator);
	}

	/**
	 * Prida novy objekt condition a oprerator, ktery bude ve vyslednem dotazu p�ed
	 * pridavanou podminkou.
	 * 
	 * @param condition - condition
	 * @param operator - operator
	 * 
	 * @return vraci objekt typu UniQLJoin
	 */
	public UniQLJoin addCondition(String operator, UniQLCondition cf) {
		if (this.criterions == null) this.criterions = new UniQLCriterion();
		this.criterions.setLastConditionOperator(operator);
		this.criterions.addCondition(cf);
		return this;
	}

	/*
	 * Metoda je shodna s addCondition(String operator, UniQLCondition cf)
	 */
	public UniQLJoin add(String operator, UniQLCondition cf) {
		return addCondition(operator, cf);
	}

	/**
	 * Prida novy objekt condition a oprerator, ktery bude ve vyslednem dotazu za
	 * pridavanou podminkou a operator, ktery bude ve vyslednem dotazu pred
	 * pridavanou podminkou. 
	 * 
	 * @param op1 - operator1
	 * @param condition - condition
	 * @param op2 - operator2
	 * 
	 * @return vraci objekt typu UniQLJoin
	 */
	public UniQLJoin addCondition(String operator1, UniQLCondition cf, String operator2) {
		addCondition(operator1, cf);
		this.criterions.setLastConditionOperator(operator2);
		return this;
	}

	/*
	 * Metoda je shodna s addCondition(String operator1, UniQLCondition cf, String operator2)
	 */
	public UniQLJoin add(String operator1, UniQLCondition cf, String operator2) {
		return addCondition(operator1, cf, operator2);
	}

	// -----  -----------------------------------------------------------------
	
	/**
	 * Prida novy objekt UniQLFrom a alias. Parametr name reprezentuje jmeno tabulky,
	 * alias zastupme jmeno (Alias).  
	 * 
	 * @param name - name
	 * @param alias - alias
	 * 
	 * @return vraci objekt typu UniQLJoin
	 */
	public UniQLJoin addFrom(String name, String alias) {
		this.from = new UniQLFrom(name, alias);
		this.from.setDialect(this.getDialect());
		return this;
	}
	
	/**
	 * Prida novy objekt UniQLFrom. Parametr name reprezentuje jmeno tabulky.
	 * 
	 * @param name - name
	 * 
	 * @return vraci join
	 */
	public UniQLJoin addFrom(String name) {
		this.from = new UniQLFrom(name);
		this.from.setDialect(this.getDialect());
		return this;
	}
	
	/**
 	 * Prida novy objekt UniQLFrom. Parametr je jiz drive vytvoreny objekt UniQLFrom.
	 * 
	 * @param from - from
	 * 
	 * @return vraci join
	 */
	public UniQLJoin addFrom(UniQLExpr from) {
		this.from = from;
		return this;
	}
	
	/**
	 * Navraci objekt UniQLFrom.
	 * 
	 * @return - vraci objekt UniQLFrom
	 */
	public UniQLExpr getFrom() {
		if (this.from != null) this.from.setDialect(this.getDialect());
		return this.from;
	}

	/**
	 * Metoda vraci jmeno tabulky pridelene metodou addFrom
	 * @return
	 */
	public String getTable() {
		if (this.from != null) {			
			if (this.from instanceof UniQLFrom) {				
				String tableName = ((UniQLFrom)this.from).getTable();
				return tableName;
			}
		}
		return null;
	}

	/**
	 * Metoda vraci alias tabulky pridelene metodou addFrom
	 * @return
	 */
	public String getAlias() {
		if (this.from != null) {
			if (this.from instanceof UniQLFrom) {
				String alias = ((UniQLFrom)this.from).getAlias();
				if (alias == null) alias = ((UniQLFrom)this.from).getTable();
				return alias;
			} else if (this.from instanceof UniQLSubQuery) {
				String alias = ((UniQLSubQuery)this.from).getAlias();
				return alias;
			}
		}
		return null;
	}

	// -----  -----------------------------------------------------------------

	/**
	 * Zadava hodnotu type. Parameterm mohou b�t konstanty objektu UniQLJoin 
	 * (napr. LEFT_JOIN). Implicitne je type nastaven na UniQLJoin.LEFT_JOIN. 
	 * 
	 * @param type - type
	 * 
	 * @return vraci join
	 */
	public UniQLJoin setType(int type) {
		this.type = type;
		return this;
	}

	/**
	 * Navraci hodnotu promenne type.
	 * 
	 * @return - hodnotu promenne type.
	 */
	public int getType() {
		return this.type;
	}

	/**
	 * Navraci stringove vyjadreni typu objektu.
	 * 
	 * @return - stringove vyjadreni typu objektu.
	 */
	public String getTypeString() {
		switch (getType()) {
			case 0: return "INNER";
			case 2: return "RIGTH";
			case 4: return "FULL";
			case 1:
			default: {
				return "LEFT";
			}
		}
	}
	
	/**
	 * Navraci kontener podninek criterions.
	 * 
	 * @return - kontener podninek criterions.
	 */
	public UniQLCriterion getCriterions() {
		return criterions;
	}

	/**
	 * Nahradi stavajici criterions za novou.
	 * 
	 * @param novy kontejner conditions
	 */
	public void setCriterions(UniQLCriterion criterions) {
		this.criterions = criterions;
	}

	public void addCriterions(UniQLCriterion c) {
		addCriterions(c, null); 
	}

	public void addCriterions(UniQLCriterion c, String op) {
		if (c == null) return;
		this.criterions.addCriterions(c);
		if (op != null) this.criterions.setLastConditionOperator(op); 
	}

	public void addCriterions(String op ,UniQLCriterion c) {
		if (op != null) this.criterions.setLastConditionOperator(op);
		addCriterions(c);
	}

	// -----  -----------------------------------------------------------------
	
	/* (non-Javadoc)
	 * @see cz.acies.uniql.sql.UniQLExpr#transToXML()
	 */
	public String transToXML() throws Exception {
		DOMElement node = new DOMElement("UniQLJoin");
		transToXML(node);
		return node.asXML();
	}
	
	/* (non-Javadoc)
	 * @see cz.acies.uniql.sql.UniQLExpr#transToXML(org.dom4j.dom.DOMElement)
	 */
	public DOMElement transToXML(DOMElement node) throws Exception {
		node.setName("UniQLJoin");
		if (this.from != null) {
			DOMElement child = this.from.transToXML(new DOMElement("UniQLFrom"));
			//System.out.println("UniQLJoin.transToXML().child = "+child);
			node.appendChild(child);
			if (criterions != null) {
				DOMElement childs = criterions.transToXML(new DOMElement("UniQLExpr"));
				node.appendChild(childs);
			}
		}
		return node;
	}
	
	/* (non-Javadoc)
	 * @see cz.acies.uniql.sql.UniQLExpr#xmlToExpr(java.lang.String)
	 */
	public UniQLExpr xmlToExpr(String xml) throws Exception {
		System.out.println("UniQLJoin.xmlToExpr(xml)");
		return null;
	}

	/* (non-Javadoc)
	 * @see cz.acies.uniql.sql.UniQLExpr#xmlToExpr(org.dom4j.Element)
	 */
	@Override
	@SuppressWarnings("unchecked")
	public UniQLExpr xmlToExpr(Element node) throws Exception {
		ArrayList<Element> params = new ArrayList<Element>(node.elements());
		for (int i = 0; i < params.size(); i++) {
			Element child = (Element)params.get(i);
			String nodename = child.getName();
			UniQLExpr expr = null;
			if (nodename.trim().equals("UniQLCriterion")) {
				expr = new UniQLCriterion();
				expr.xmlToExpr(child);
				if (expr != null) {
					this.setCriterions((UniQLCriterion)expr);
				}				
			} else if (nodename.trim().equals("UniQLFrom")) {
				expr = new UniQLFrom();
				expr.xmlToExpr(child);
				if (expr != null) {
					this.addFrom((UniQLFrom)expr);
				}				
			} else if (nodename.trim().equals("UniQLSubQuery")) {
				expr = new UniQLSubQuery();
				expr.xmlToExpr(child);
				if (expr != null) {
					this.addFrom((UniQLSubQuery)expr);
				}				
				
			}
		}
		return this;
	}
	
	// -----  -----------------------------------------------------------------
	
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	public String toString() {
		UniQLExpr expr = this.getFrom();
		String s = "";
		if (expr instanceof UniQLFrom) {
			UniQLFrom from = (UniQLFrom)expr; 
			s += "from: " + from.getName();
			s += "alias: " + from.getAlias();
		} 
		return "" + this.getClass().getSimpleName() + "( " + s + " )";
	}
	
}
